// /////////////////////////////////////////////////////////////////////////////
//
// Name:            Script.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           The Script class is technically a wrapper for every
//                  AngelScript file that is loaded by the framework.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "Script.h"

#ifdef __COMPILE_WITH_ANGELSCRIPT__

#include "../core/GameManager.h"


// Script class
// Static variables.
u32 Script::mIDCount = 0;

// Constructor of the Script class.
Script::Script(const std::string &name)
: mName(name), mModule(NULL)
{
    mID = mIDCount++;

    // Get a pointer to the Script Manager.
    ScriptManager *scriptMgr = GameManager::Instance()->getScriptManager();

    // Create a script module and context.
    mModule = scriptMgr->getEngine()->GetModule(mName.c_str(), asGM_ALWAYS_CREATE);
    mContext = scriptMgr->getEngine()->CreateContext();
}

// Deconstructor of the Script class.
Script::~Script()
{
    // Get a pointer to the Script Manager.
    ScriptManager *scriptMgr = GameManager::Instance()->getScriptManager();

    // Clean up.
    if(mModule != NULL)
      scriptMgr->getEngine()->DiscardModule(mName.c_str());

    mContext->Release();
}

// Gets the ID of this script.
u32 Script::getID() const
{
    return mID;
}

// Gets the name of this script.
const std::string& Script::getName() const
{
    return mName;
}

// Returns the AngelScript context associated with this Script object.
asIScriptContext* Script::getContext() const
{
    return mContext;
}

// Gets the AngelScript module associated with this Script object.
asIScriptModule* Script::getModule() const
{
    return mModule;
}

// Loads a script file and builds it.
bool Script::loadScript(const std::string &fileName)
{
    // Get pointers to sub-systems of the Game Manager and Irrlicht.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();
    ScriptManager *scriptMgr = GameManager::Instance()->getScriptManager();

    // Reset previous data.
    if(mModule != NULL)
    {
        scriptMgr->getEngine()->DiscardModule(mName.c_str());
        mModule = NULL;
    }

    // Does the file exist?
    if(!fileSystem->existFile(fileName.c_str()))
      return false;

    // Create the module.
    mModule = scriptMgr->getEngine()->GetModule(mName.c_str(), asGM_ALWAYS_CREATE);

    // Open the given file and load the contents to the buffer.
    IReadFile *file = fileSystem->createAndOpenFile(fileName.c_str());

    long fSize = file->getSize();
    c8 * fBuffer = new c8[fSize+1];
    memset(fBuffer, 0, fSize+1);

    file->read(fBuffer, fSize);
    file->drop();

    // Add a script section to the earlier created module.
    mModule->AddScriptSection("main", fBuffer);
    scriptMgr->addScriptSectionsToScript(this);

    // Build the script.
    int r = mModule->Build();
    if(r < 0)
      return false;

    // Clean up.
    delete[] fBuffer;

    return true;
}

// Loads a script file and builds it.
bool Script::loadScript(IReadFile *file)
{
    // Get pointers to sub-systems of the Game Manager.
    ScriptManager *scriptMgr = GameManager::Instance()->getScriptManager();

    // Reset previous data.
    if(mModule != NULL)
    {
        scriptMgr->getEngine()->DiscardModule(mName.c_str());
        mModule = NULL;
    }

    // Is the pointer valid?
    if(!file)
      return false;

    // Create the module.
    mModule = scriptMgr->getEngine()->GetModule(mName.c_str(), asGM_ALWAYS_CREATE);

    // Read from given file and load the contents to the buffer.
    long fSize = file->getSize();
    c8 * fBuffer = new c8[fSize+1];
    memset(fBuffer, 0, fSize+1);

    file->read(fBuffer, fSize);

    // Add a script section to the earlier created module.
    scriptMgr->addScriptSectionsToScript(this);
    mModule->AddScriptSection("main", fBuffer);

    // Build the script.
    int r = mModule->Build();
    if(r < 0)
      return false;

    // Clean up.
    delete[] fBuffer;

    return true;
}

// Unloads the loaded script.
bool Script::unloadScript()
{
    ScriptManager *scriptMgr = GameManager::Instance()->getScriptManager();

    if(mModule != NULL)
    {
        scriptMgr->getEngine()->DiscardModule(mName.c_str());
        mModule = NULL;
        return true;
    }

    return false;
}

// Executes the function with the given name and with the given arguments.
bool Script::executeFunctionByName(const std::string &name, const std::string &args)
{
    // Do we have a module?
    if(mModule == NULL)
      return false;

    // Get the ID of the main function.
    int functionID = mModule->GetFunctionIdByName(name.c_str());

    if(functionID == asERROR || functionID == asMULTIPLE_FUNCTIONS || functionID == asNO_FUNCTION)
      return false;

    // Prepare the function.
    std::stringstream ss;
    ss << name << "(" << args << ");";

    mModule->GetEngine()->ExecuteString(mName.c_str(), ss.str().c_str(), &mContext,
                                        asEXECSTRING_ONLY_PREPARE | asEXECSTRING_USE_MY_CONTEXT);

    // Execute the function.
    int r = mContext->Execute();
    if(r == asEXECUTION_FINISHED)
    {
        return true;
    }

    else return false;
}

#endif // __COMPILE_WITH_ANGELSCRIPT__

// End of File
